/**
 * $Revision: 3034 $
 * $Date: 2005-11-04 21:02:33 -0300 (Fri, 04 Nov 2005) $
 *
 * Copyright (C) 2004-2008 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jawa.core.container;

import java.io.File;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
import org.jawa.core.JawaServer;
import org.jawa.core.util.JawaGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The admin console plugin. It starts a Jetty instance on the configured
 * port and loads the admin console web application.
 *
 * @author Matt Tucker
 */
public class AdminConsolePlugin implements Plugin {

	private static final Logger Log = LoggerFactory.getLogger(AdminConsolePlugin.class);

	private int adminPort;
	private Server adminServer;
	private ContextHandlerCollection contexts;
	private boolean restartNeeded = false;
	private boolean enabled = false;

	private File pluginDir;

	/**
	 * Create a Jetty module.
	 */
	public AdminConsolePlugin() {
		contexts = new ContextHandlerCollection();

		// JSP 2.0 uses commons-logging, so also override that implementation.
		System.setProperty("org.apache.commons.logging.LogFactory", "org.jawa.core.util.log.CommonsLogFactory");
	}

	/**
	 * Starts the Jetty instance.
	 */
	public void startup() {
		restartNeeded = false;

		adminPort = JawaGlobals.getXMLProperty("console.port", 9090);
		adminServer = new Server();
		final QueuedThreadPool tp = new QueuedThreadPool(300);
		tp.setName("Jetty-JawaConsole");
		adminServer.setThreadPool(tp);

		// Do not send Jetty info in HTTP headers
		adminServer.setSendServerVersion(false);

		// Create connector for http traffic if it's enabled.
		if (adminPort > 0) {
			Connector httpConnector = new SelectChannelConnector();
			// Listen on a specific network interface if it has been set.
			String bindInterface = getBindInterface();
			httpConnector.setHost(bindInterface);
			httpConnector.setPort(adminPort);
			adminServer.addConnector(httpConnector);
		}

		// Create a connector for https traffic if it's enabled.
		// Make sure that at least one connector was registered.
		if (adminServer.getConnectors() == null || adminServer.getConnectors().length == 0) {
			adminServer = null;
			// Log warning.
			log("控制台启动出错,端口设置错误");
			return;
		}

		HandlerCollection collection = new HandlerCollection();
		adminServer.setHandler(collection);
		collection.setHandlers(new Handler[] { contexts, new DefaultHandler() });

		try {
			adminServer.start();
		} catch (Exception e) {
			Log.error("Could not start admin conosle server", e);
		}

		// Log the ports that the admin server is listening on.
		logAdminConsolePorts();
	}

	/**
	 * Shuts down the Jetty server.
	 * */
	public void shutdown() {
		try {
			if (adminServer != null && adminServer.isRunning()) {
				adminServer.stop();
			}
		} catch (Exception e) {
			Log.error("Error stopping admin console server", e);
		}
		adminServer = null;
	}

	public void initializePlugin(PluginManager manager, File pluginDir) {
		this.pluginDir = pluginDir;

		//是否禁用了控制台
		enabled = JawaGlobals.getXMLProperty("console.enabled", false);
		if (enabled) {
			createWebAppContext();

			startup();
		}

	}

	public void destroyPlugin() {
		shutdown();
	}

	/**
	 * Returns true if the Jetty server needs to be restarted. This is usually required when
	 * certificates are added, deleted or modified or when server ports were modified.
	 *
	 * @return true if the Jetty server needs to be restarted.
	 */
	public boolean isRestartNeeded() {
		return restartNeeded;
	}

	/**
	 * Returns <tt>null</tt> if the admin console will be available in all network interfaces of this machine
	 * or a String representing the only interface where the admin console will be available.
	 *
	 * @return String representing the only interface where the admin console will be available or null if it
	 * will be available in all interfaces.
	 */
	public String getBindInterface() {
		String adminInterfaceName = JawaGlobals.getXMLProperty("adminConsole.interface");
		String globalInterfaceName = JawaGlobals.getXMLProperty("network.interface");
		String bindInterface = null;
		if (adminInterfaceName != null && adminInterfaceName.trim().length() > 0) {
			bindInterface = adminInterfaceName;
		} else if (globalInterfaceName != null && globalInterfaceName.trim().length() > 0) {
			bindInterface = globalInterfaceName;
		}
		return bindInterface;
	}

	/**
	 * Returns the non-SSL port on which the admin console is currently operating.
	 *
	 * @return the non-SSL port on which the admin console is currently operating.
	 */
	public int getAdminUnsecurePort() {
		return adminPort;
	}

	/**
	 * Returns the collection of Jetty contexts used in the admin console. A root context "/"
	 * is where the admin console lives. Additional contexts can be added dynamically for
	 * other web applications that should be run as part of the admin console server
	 * process. The following pseudo code demonstrates how to do this:
	 *
	 * <pre>
	 *   ContextHandlerCollection contexts = ((AdminConsolePlugin)pluginManager.getPlugin("admin")).getContexts();
	 *   context = new WebAppContext(SOME_DIRECTORY, "/CONTEXT_NAME");
	 *   contexts.addHandler(context);
	 *   context.setWelcomeFiles(new String[]{"index.jsp"});
	 *   context.start();
	 * </pre>
	 *
	 * @return the Jetty handlers.
	 */
	public ContextHandlerCollection getContexts() {
		return contexts;
	}

	public void restart() {
		try {
			adminServer.stop();
			adminServer.start();
		} catch (Exception e) {
			Log.error(e.getMessage(), e);
		}
	}

	private void createWebAppContext() {
		ServletContextHandler context;
		if (JawaGlobals.getXMLProperty("console.developmentMode",false)) {
			System.out.println("admin.console.devmode");
			context = new WebAppContext(contexts, pluginDir.getParentFile().getParentFile().getParentFile().getParentFile().getParent()
					+ File.separator + "src" + File.separator + "main" + File.separator + "webapp", "/");
		} else {
			context = new WebAppContext(contexts, pluginDir.getAbsoluteFile() + File.separator + "webapp", "/");
		}
		context.setWelcomeFiles(new String[] { "index.jsp" });
	}

	private void log(String string) {
		Log.info(string);
		System.out.println(string);
	}

	private void logAdminConsolePorts() {
		String listening = "console:";
		String hostname = getBindInterface() == null ? JawaServer.getInstance().getServerInfo().getHostName()
				: getBindInterface();
		boolean isPlainStarted = false;
		for (Connector connector : adminServer.getConnectors()) {
			if (connector.getPort() == adminPort) {
				isPlainStarted = true;
			} 
		}

		log(listening + " http://" + hostname + ":" + adminPort);
	}
}
